﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Needle.Utils.Collections.Threadsafe
{

    /// <summary></summary>
    public class LockedQueue<T> : IQueue<T>
    {

        #region Node

        private class Node
        {
            public Node(T item) 
            { this.Item = item; }

            public T Item;
            public Node Next;

        }

        #endregion
        /// <summary>
        /// Hold the old head, _head.Next holds the real head Node.
        /// </summary>
        private Node _head;
        /// <summary>
        /// Holds the tail.
        /// </summary>
        private Node _tail;
        private readonly object _lock;

        /// <summary></summary>
        public LockedQueue()
            : this(new object())
        { }

        /// <summary></summary>
        public LockedQueue(object mutex)
        {
            if (mutex == null)
                throw new ArgumentNullException("mutex", "mutex is null.");
            _lock = mutex;
            _head = new Node(default(T));
            _tail = _head;
        }

        private int _count;
        /// <summary></summary>
        public int Count
        {
            get
            {
                lock (_lock)
                {
                    return _count;
                }
            }
        }

        /// <summary></summary>
        public bool TryEnqueue(T item) 
        {
            Enqueue(item);
            return true;
        }

        /// <summary></summary>
        public void Enqueue(T item)
        {
            Node newNode = new Node(item);
            lock (_lock)
            {
                _tail.Next = newNode;
                _tail = newNode;
                _count++;
            }
        }

        /// <summary></summary>
        public bool TryDequeue(out T item)
        {
            // clear the item location.
            item = default(T);
            lock (_lock)
            {
                if (_head.Next != null)
                {
                    _head = _head.Next;
                    item = _head.Item;
                    // clear item, so we don't hang on to it.
                    _head.Item = default(T);
                    _count--;
                    return true;
                }
                // queue empty...
                return false;
            }
        }

        /// <summary></summary>
        public T Dequeue()
        {
            T result;
            if (!TryDequeue(out result))
                throw new InvalidOperationException("Queue empty.");
            return result;
        }

    }
}
